Die Giftspritze für Webapplikationen - Injects (Teil I)
Jeder, der eine Website mit selbstgeschriebenen PHP Skripts oder gar AJAX und anderen
interaktiven Technologien gebastelt hat, und das Glück hatte viele Besucher zu haben wird sich auch immer
wieder mit Sicherheitsbedenken konfrontiert sehen.
Dabei helfen einige kleine Tricks, sauberer Sourcecode und das Verstehen wie die ausführende Technologie funktioniert schon bei der Entwicklung enorm weiter.
Ich werde hier kurz die wichtigsten Eckpunkte zusammenfassen:
* Cookies: Eine angenehme Technologie, die allerdings vom Benutzer verändert werden kann und deshalb prinzipiell auf Plausibilität geprüft werden sollte. Oftmals werden Cookies als Sessions verwendet. Hier übernimmt der Webserver (meistens Apache) schon die Absicherung.
Sobald jedoch weitere Daten gespeichert werden, sollte man diese escapen.
Beispiel: Die Applikation speichert den Wert «Username» in einem Cookie als Stringwert und lädt die Benutzerinfos aus einer MySQL Datenbank dynamisch nach. Ein versierter Angrifer manipuliert das Cookie wie folgt: Statt «foo-bar» enthält das Cookie nun den String Wert "'; DROP ALL DATABASESM;".
Mit etwas Glück für den Angreifer sieht das PHP Script nun so aus:
Wie sieht das aber nun für die MySQL Datenbank aus?
So nämlich:
Ja nach Berechtigung des angemeldeten Datenbankbenutzers passiert entweder gar nichts oder es sind alle Datenbanken futsch.
Bestenfalls wird ein Backup dies alles beheben, schlechtestenfalls wird man wohl oder übel die Benutzerdaten im Datennirvana beerdigen dürfen.
Die sauberste Lösung für dieses Problem ist die Verwendung eines Session-Cookies, also der PHP internen Sessionfunktion.
Die Sessiondaten können vom Angreifer nicht verändert werden (ausser er besitzt bereits Serverzugriff — aber dann hat man mit anderen Problemen zu kämpfen).
Zwar kann auf der Clientseite die SessionID verändert werden, diese ist aber lediglich ein randomisierter MD5-Hash. Hier etwas zu injecten ist nicht sinnvoll.
* Umgang mit Benutzereingaben
Oftmals müssen Benutzereingaben jedoch in die Datenbank eingetragen müssen (Beispielsweise ein Gästebuch oder ähnliches).
Hier hilft die PHP Funktion
Diese Funktion maskiert alle MySQL relevante Zeichen (aus "'" wird dann"\'") und verhindert damit eine Injection.
Eine neue Funktion von MySQL schafft hier auch Abhilfe:
Stored Routines.
Bei MS SQL schon länger unter «Stored Procedures» bekannt hält nun seit MySQL 5.1 Einzug.
Bereits definierten Queries werden nur noch Parameter übergeben. Hierdurch wird sehr wirkungsvoll verhindert, dass Angreifer
ausführbare Queries injecten, da die SQL Engine gar nicht auf die Idee kommt, dass hier ein Query enthalten sein könnte.
Ausführbarer Code und Payload werden also ähnlich der getrennt.
Hier ist die Referenz zu Stored Routines :.
Eine Projekt, für die, die das Glück haben root-Zugang zu ihrem Webserver zu haben:
Das .
IDS steht für Intrusion Detection System. Das PHPIDS überprüft alle Benutzereingaben auf Schädlichkeit und entdeckt dabei neben gängigen MySQL Injects auch Corss-Site-Script-Injects (XSS) und andere HTML Injects.
Auch unbekannte Angriffe werden durch Regulare Expressions zuverlässig gefunden. Je nach von PHP IDS zurückgelieferter Impact-Bewertung der HTTP Anfrage kann man entsprechend mit dem angreifendem Host umgehen.
Sinnvoll ist z.B. ein DENY FROM Eintrag in der .htaccess Datei.
* Umgang mit hochgeladenen Dateien
Immer wichtiger bei Web (2.0) Angeboten wird Benutzercontent.
Die Gefahr jedoch hierbei ist, dass ein Angreifer php-Code hochlädt oder den Dateinamen sabotiert, so dass die Datei einen anderen Weg einschlägt als wir es geplant hatten.
Folgendes ist hierbei zu beachten:
Der von php übergebene Dateimetadaten ($_FILE[][]) können zum Großteil vom Benutzer beinflusst werden.
So ist es sinnvoller, die Dateimetadaten in einer MySQL Datenbank zu speichern (Wie oben beschrieben — mit Stored Routines) und
die Datein mit einer MySQL-UniqueID zu versehen, diese Unique ID kann dann als (Unix-) Dateiname verwendet werden.
Beim zurückgeben der Daten an den Benutzer bedienen wir uns der Header-Manipulation.
Beispiel: stream.php
Durch die Manipulation des Headers, dieht es für den Browser aus wie die Original Datei.
Jedoch kommt Apache hier nicht auf die Idee, diese Datei auszuführen.
Die Dateien der Benutzer sollten im übrigen in einem Verzeichnis liegen, dessen .htaccess wie folgt aussieht:
Über mod_rewrite können wir auch noch den Pfad so verändern, dass ein Link so aussieht:
Dieses Script überprüft zusätzlich ob die übergebene Datei-ID (SID — Stream Identifier) auch nur aus Zahlen besteht.
Auch hier wird die Abstraktion des Payloads hoffentlich klar ersichtlich.
So long — Keyhugger
(In Teil II werde ich mich dem Hardening von Webapplikationen und dem Umgang mit kompromittierten Systemen widmen :))
interaktiven Technologien gebastelt hat, und das Glück hatte viele Besucher zu haben wird sich auch immer
wieder mit Sicherheitsbedenken konfrontiert sehen.
Dabei helfen einige kleine Tricks, sauberer Sourcecode und das Verstehen wie die ausführende Technologie funktioniert schon bei der Entwicklung enorm weiter.
Ich werde hier kurz die wichtigsten Eckpunkte zusammenfassen:
* Cookies: Eine angenehme Technologie, die allerdings vom Benutzer verändert werden kann und deshalb prinzipiell auf Plausibilität geprüft werden sollte. Oftmals werden Cookies als Sessions verwendet. Hier übernimmt der Webserver (meistens Apache) schon die Absicherung.
Sobald jedoch weitere Daten gespeichert werden, sollte man diese escapen.
Beispiel: Die Applikation speichert den Wert «Username» in einem Cookie als Stringwert und lädt die Benutzerinfos aus einer MySQL Datenbank dynamisch nach. Ein versierter Angrifer manipuliert das Cookie wie folgt: Statt «foo-bar» enthält das Cookie nun den String Wert "'; DROP ALL DATABASESM;".
Mit etwas Glück für den Angreifer sieht das PHP Script nun so aus:
[...]
// Hier werden die Benutzerdaten aus dem Cookie nachgeladen.
$ergebnis=mysql_query('SELECT * FROM 'usr_data' WHERE 'UserName' = '.$HTTP_COOKIE_VARS["Username"]);
$benutzerdaten=mysql_fetch_array($ergebnis);
[...]
Wie sieht das aber nun für die MySQL Datenbank aus?
So nämlich:
SELECT * FROM 'usr_data' WHERE 'UserName' = '' ; DROP ALL DATABASES;Ja nach Berechtigung des angemeldeten Datenbankbenutzers passiert entweder gar nichts oder es sind alle Datenbanken futsch.
Bestenfalls wird ein Backup dies alles beheben, schlechtestenfalls wird man wohl oder übel die Benutzerdaten im Datennirvana beerdigen dürfen.
Die sauberste Lösung für dieses Problem ist die Verwendung eines Session-Cookies, also der PHP internen Sessionfunktion.
Die Sessiondaten können vom Angreifer nicht verändert werden (ausser er besitzt bereits Serverzugriff — aber dann hat man mit anderen Problemen zu kämpfen).
Zwar kann auf der Clientseite die SessionID verändert werden, diese ist aber lediglich ein randomisierter MD5-Hash. Hier etwas zu injecten ist nicht sinnvoll.
* Umgang mit Benutzereingaben
Oftmals müssen Benutzereingaben jedoch in die Datenbank eingetragen müssen (Beispielsweise ein Gästebuch oder ähnliches).
Hier hilft die PHP Funktion
string mysql_escape_string ( string $unescaped_string )Diese Funktion maskiert alle MySQL relevante Zeichen (aus "'" wird dann"\'") und verhindert damit eine Injection.
Eine neue Funktion von MySQL schafft hier auch Abhilfe:
Stored Routines.
Bei MS SQL schon länger unter «Stored Procedures» bekannt hält nun seit MySQL 5.1 Einzug.
Bereits definierten Queries werden nur noch Parameter übergeben. Hierdurch wird sehr wirkungsvoll verhindert, dass Angreifer
ausführbare Queries injecten, da die SQL Engine gar nicht auf die Idee kommt, dass hier ein Query enthalten sein könnte.
Ausführbarer Code und Payload werden also ähnlich der getrennt.
Hier ist die Referenz zu Stored Routines :.
Eine Projekt, für die, die das Glück haben root-Zugang zu ihrem Webserver zu haben:
Das .
IDS steht für Intrusion Detection System. Das PHPIDS überprüft alle Benutzereingaben auf Schädlichkeit und entdeckt dabei neben gängigen MySQL Injects auch Corss-Site-Script-Injects (XSS) und andere HTML Injects.
Auch unbekannte Angriffe werden durch Regulare Expressions zuverlässig gefunden. Je nach von PHP IDS zurückgelieferter Impact-Bewertung der HTTP Anfrage kann man entsprechend mit dem angreifendem Host umgehen.
Sinnvoll ist z.B. ein DENY FROM Eintrag in der .htaccess Datei.
* Umgang mit hochgeladenen Dateien
Immer wichtiger bei Web (2.0) Angeboten wird Benutzercontent.
Die Gefahr jedoch hierbei ist, dass ein Angreifer php-Code hochlädt oder den Dateinamen sabotiert, so dass die Datei einen anderen Weg einschlägt als wir es geplant hatten.
Folgendes ist hierbei zu beachten:
Der von php übergebene Dateimetadaten ($_FILE[][]) können zum Großteil vom Benutzer beinflusst werden.
So ist es sinnvoller, die Dateimetadaten in einer MySQL Datenbank zu speichern (Wie oben beschrieben — mit Stored Routines) und
die Datein mit einer MySQL-UniqueID zu versehen, diese Unique ID kann dann als (Unix-) Dateiname verwendet werden.
Beim zurückgeben der Daten an den Benutzer bedienen wir uns der Header-Manipulation.
Beispiel: stream.php
<?php
if ( ereg ( '[0-9]{1,9}', $_GET['sid']) ) $fid=$_GET['sid'];
$sql='SELECT * FROM UserFiles WHERE id = '.mysql_escape_string($fid);
$result=mysql_query($sql);
$row=mysql_fetch_array($result);
if(!($row['id']==''))
{
if(file_exists($row['filepath']))
{
header('Content-type: '.$row['fmime']);
header('Content-Disposition: attachment; filename="'.$row['filename'].'"');
header('Content-Length: '.filesize($row['filepath']));
header("Cache-Control: public, must-revalidate");
readfile($row['filepath']);
}
}
?>
Durch die Manipulation des Headers, dieht es für den Browser aus wie die Original Datei.
Jedoch kommt Apache hier nicht auf die Idee, diese Datei auszuführen.
Die Dateien der Benutzer sollten im übrigen in einem Verzeichnis liegen, dessen .htaccess wie folgt aussieht:
DENY FROM ALLÜber mod_rewrite können wir auch noch den Pfad so verändern, dass ein Link so aussieht:
Dieses Script überprüft zusätzlich ob die übergebene Datei-ID (SID — Stream Identifier) auch nur aus Zahlen besteht.
Auch hier wird die Abstraktion des Payloads hoffentlich klar ersichtlich.
So long — Keyhugger
(In Teil II werde ich mich dem Hardening von Webapplikationen und dem Umgang mit kompromittierten Systemen widmen :))











Kommentare (6)
Das Schöne am Cookie ist natürlich die Wiedererkennbarkeit, die bei PHPSESSID nicht zwangsläufig gegeben ist.
Bzgl. des Intrusion Detection System: ist noch ein hübsches Ding. Setzt eine Ebene tiefer an.
BuzzEins
PHPSESSID hat halt den Vorteil, dass PHP alles für dich erledigt :).
SNORT ist auch hübsch, PHPIDS ist halt sehr spezialisiert :)
keyhugger
admin
Schöner Artikel jedenfalls, eine solche Übersicht hätte ich mir in meiner Anfangszeit auch gewünscht. Freu mich auch schon auf die Fortsetzung!
Nathrai
admin
ad Webworker: Vor gefühlten ewigen Zeiten mal, bin aber inzwischen in die SAP-Entwicklung umgestiegen (dort natürlich hauptsächlich im Bereich Webtechnologien). Echte Webentwicklung spielt's leider nur noch in der spärlichen Freizeit.
Nathrai